﻿using System;
using System.IO;
using GACore.Progress;

namespace GACore
{
	public class Controller
	{
		private Preferences preferences;
		private Problem problem;

		private History history = new History();
		private string directoryName;

		#region Singleton
		private static Controller instance = new Controller();
		public static Controller Instance
		{
			get { return Controller.instance; }
			set { Controller.instance = value; }
		}

		private Controller() { }

		#endregion

		public void Init(Preferences preferences, Problem problem)
		{
			this.preferences = preferences;
			this.problem = problem;
			history.AddEntry(problem.Init(preferences));
			InitHistory();
		}

		private void InitHistory()
		{
			this.directoryName = problem.GetProblemName() + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
			Directory.CreateDirectory(directoryName);
			SaveHistoryEntry();
		}

		public void Optimize()
		{
			Console.CancelKeyPress += Console_CancelKeyPress;
			while (NotAbort())
			{
				history.AddEntry(problem.ComputeStep());
				SaveHistoryEntry();
			}
			FinishOptimization(false);
		}

		private void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
		{
			FinishOptimization(true);
		}

		private void FinishOptimization(bool isAborted)
		{
			string cout = isAborted ? "Die Optimierung wurde abgebrochen. " : "Es wurde ein Optimum gefunden. ";
			Console.WriteLine(cout + "Der Optimierungsverlauf kann im Ordner " + directoryName + " eingesehen werden.");
			Console.WriteLine("Beste Lösung hat Fitness:" + history.BestOverallSolution.Fitness);
			File.WriteAllText(directoryName + "/" + problem.GetProblemName() + ", Beste Lösung.txt",
				history.BestOverallSolution.ToString() + Environment.NewLine + "Fitness der besten Lösung: " + history.BestOverallSolution.Fitness);
		}

		private bool NotAbort()
		{
			if (preferences.BestFitness == history.Current.BestVector.Fitness)
			{
				return false;
			}
			for (int i = history.Current.Step; i >= 0 && i >= history.Current.Step - 20; i--)
			{
				for (int j = 0; j < history[i].Population.Length; j++)
				{
					if (history.Current.Step < 20 || history[i].Population[j].Fitness != history.Current.Population[j].Fitness)
					{
						return true;
					}
				}
			}
			return false;
		}

		private void SaveHistoryEntry()
		{
			Console.WriteLine(history.Current.ToShortString());
			string filename = directoryName + "/" + problem.GetProblemName() + ", Schritt " + history.Current.Step + ".txt";
			string content = history.Current.ToLongString() + Environment.NewLine + Environment.NewLine + problem.GetProblemRepresentation();
			File.WriteAllText(filename, content);
		}

	}
}
